﻿using System;
using System.Collections.Generic;

namespace FirstFloor.Documents.Pdf.Fonts
{
    /// <summary>
    /// Maps characters to glyph names.
    /// </summary>
    internal class CharacterToGlyphNameMap
    {
        private Dictionary<int, string> map = new Dictionary<int, string>();
        private Dictionary<string, int> mapBack = new Dictionary<string,int>();

        static CharacterToGlyphNameMap()
        {
            StandardEncoding = new CharacterToGlyphNameMap();
            MacRomanEncoding = new CharacterToGlyphNameMap();
            MacOSRomanEncoding = new CharacterToGlyphNameMap();     // slight variation on MacRoman (see also PDF Reference, version 1.7, page 431) 
            WinAnsiEncoding = new CharacterToGlyphNameMap();
            PdfDocEncoding = new CharacterToGlyphNameMap();

            Add("A", 65, 65, 65, 65);
            Add("AE", 225, 174, 198, 198);
            Add("Aacute", -1, 231, 193, 193);
            Add("Acircumflex", -1, 229, 194, 194);
            Add("Adieresis", -1, 128, 196, 196);
            Add("Agrave", -1, 203, 192, 192);
            Add("Aring", -1, 129, 197, 197);
            Add("Atilde", -1, 204, 195, 195);
            Add("B", 66, 66, 66, 66);
            Add("C", 67, 67, 67, 67);
            Add("Ccedilla", -1, 130, 199, 199);
            Add("D", 68, 68, 68, 68);
            Add("E", 69, 69, 69, 69);
            Add("Eacute", -1, 131, 201, 201);
            Add("Ecircumflex", -1, 230, 202, 202);
            Add("Edieresis", -1, 232, 203, 203);
            Add("Egrave", -1, 233, 200, 200);
            Add("Eth", -1, -1, 208, 208);
            Add("Euro", -1, -1, 128, 160);
            Add("F", 70, 70, 70, 70);
            Add("G", 71, 71, 71, 71);
            Add("H", 72, 72, 72, 72);
            Add("I", 73, 73, 73, 73);
            Add("Iacute", -1, 234, 205, 205);
            Add("Icircumflex", -1, 235, 206, 206);
            Add("Idieresis", -1, 236, 207, 207);
            Add("Igrave", -1, 237, 204, 204);
            Add("J", 74, 74, 74, 74);
            Add("K", 75, 75, 75, 75);
            Add("L", 76, 76, 76, 76);
            Add("Lslash", 232, -1, -1, 149);
            Add("M", 77, 77, 77, 77);
            Add("N", 78, 78, 78, 78);
            Add("Ntilde", -1, 132, 209, 209);
            Add("O", 79, 79, 79, 79);
            Add("OE", 234, 206, 140, 150);
            Add("Oacute", -1, 238, 211, 211);
            Add("Ocircumflex", -1, 239, 212, 212);
            Add("Odieresis", -1, 133, 214, 214);
            Add("Ograve", -1, 241, 210, 210);
            Add("Oslash", 233, 175, 216, 216);
            Add("Otilde", -1, 205, 213, 213);
            Add("P", 80, 80, 80, 80);
            Add("Q", 81, 81, 81, 81);
            Add("R", 82, 82, 82, 82);
            Add("S", 83, 83, 83, 83);
            Add("Scaron", -1, -1, 138, 151);
            Add("T", 84, 84, 84, 84);
            Add("Thorn", -1, -1, 222, 222);
            Add("U", 85, 85, 85, 85);
            Add("Uacute", -1, 242, 218, 218);
            Add("Ucircumflex", -1, 243, 219, 219);
            Add("Udieresis", -1, 134, 220, 220);
            Add("Ugrave", -1, 244, 217, 217);
            Add("V", 86, 86, 86, 86);
            Add("W", 87, 87, 87, 87);
            Add("X", 88, 88, 88, 88);
            Add("Y", 89, 89, 89, 89);
            Add("Yacute", -1, -1, 221, 221);
            Add("Ydieresis", -1, 217, 159, 152);
            Add("Z", 90, 90, 90, 90);
            Add("Zcaron", -1, -1, 142, 153);
            Add("a", 97, 97, 97, 97);
            Add("aacute", -1, 135, 225, 225);
            Add("acircumflex", -1, 137, 226, 226);
            Add("acute", 194, 171, 180, 180);
            Add("adieresis", -1, 138, 228, 228);
            Add("ae", 241, 190, 230, 230);
            Add("agrave", -1, 136, 224, 224);
            Add("ampersand", 38, 38, 38, 38);
            Add("aring", -1, 140, 229, 229);
            Add("asciicircum", 94, 94, 94, 94);
            Add("asciitilde", 126, 126, 126, 126);
            Add("asterisk", 42, 42, 42, 42);
            Add("at", 64, 64, 64, 64);
            Add("atilde", -1, 139, 227, 227);
            Add("b", 98, 98, 98, 98);
            Add("backslash", 92, 92, 92, 92);
            Add("bar", 124, 124, 124, 124);
            Add("braceleft", 123, 123, 123, 123);
            Add("braceright", 125, 125, 125, 125);
            Add("bracketleft", 91, 91, 91, 91);
            Add("bracketright", 93, 93, 93, 93);
            Add("breve", 198, 249, -1, 24);
            Add("brokenbar", -1, -1, 166, 166);
            Add("bullet", 183, 165, 149, 128);
            Add("c", 99, 99, 99, 99);
            Add("caron", 207, 255, -1, 25);
            Add("ccedilla", -1, 141, 231, 231);
            Add("cedilla", 203, 252, 184, 184);
            Add("cent", 162, 162, 162, 162);
            Add("circumflex", 195, 246, 136, 26);
            Add("colon", 58, 58, 58, 58);
            Add("comma", 44, 44, 44, 44);
            Add("copyright", -1, 169, 169, 169);
            Add("currency", 168, 219, 164, 164);
            Add("d", 100, 100, 100, 100);
            Add("dagger", 178, 160, 134, 129);
            Add("daggerdbl", 179, 224, 135, 130);
            Add("degree", -1, 161, 176, 176);
            Add("dieresis", 200, 172, 168, 168);
            Add("divide", -1, 214, 247, 247);
            Add("dollar", 36, 36, 36, 36);
            Add("dotaccent", 199, 250, -1, 27);
            Add("dotlessi", 245, 245, -1, 154);
            Add("e", 101, 101, 101, 101);
            Add("eacute", -1, 142, 233, 233);
            Add("ecircumflex", -1, 144, 234, 234);
            Add("edieresis", -1, 145, 235, 235);
            Add("egrave", -1, 143, 232, 232);
            Add("eight", 56, 56, 56, 56);
            Add("ellipsis", 188, 201, 133, 131);
            Add("emdash", 208, 209, 151, 132);
            Add("endash", 177, 208, 150, 133);
            Add("equal", 61, 61, 61, 61);
            Add("eth", -1, -1, 240, 240);
            Add("exclam", 33, 33, 33, 33);
            Add("exclamdown", 161, 193, 161, 161);
            Add("f", 102, 102, 102, 102);
            Add("fi", 174, 222, -1, 147);
            Add("five", 53, 53, 53, 53);
            Add("fl", 175, 223, -1, 148);
            Add("florin", 166, 196, 131, 134);
            Add("four", 52, 52, 52, 52);
            Add("fraction", 164, 218, -1, 135);
            Add("g", 103, 103, 103, 103);
            Add("germandbls", 251, 167, 223, 223);
            Add("grave", 193, 96, 96, 96);
            Add("greater", 62, 62, 62, 62);
            Add("guillemotleft", 171, 199, 171, 171);
            Add("guillemotright", 187, 200, 187, 187);
            Add("guilsinglleft", 172, 220, 139, 136);
            Add("guilsinglright", 173, 221, 155, 137);
            Add("h", 104, 104, 104, 104);
            Add("hungarumlaut", 205, 253, -1, 28);
            Add("hyphen", 45, 45, 45, 45);
            Add("i", 105, 105, 105, 105);
            Add("iacute", -1, 146, 237, 237);
            Add("icircumflex", -1, 148, 238, 238);
            Add("idieresis", -1, 149, 239, 239);
            Add("igrave", -1, 147, 236, 236);
            Add("j", 106, 106, 106, 106);
            Add("k", 107, 107, 107, 107);
            Add("l", 108, 108, 108, 108);
            Add("less", 60, 60, 60, 60);
            Add("logicalnot", -1, 194, 172, 172);
            Add("lslash", 248, -1, -1, 155);
            Add("m", 109, 109, 109, 109);
            Add("macron", 197, 248, 175, 175);
            Add("minus", -1, -1, -1, 138);
            Add("mu", -1, 181, 181, 181);
            Add("multiply", -1, -1, 215, 215);
            Add("n", 110, 110, 110, 110);
            Add("nine", 57, 57, 57, 57);
            Add("ntilde", -1, 150, 241, 241);
            Add("numbersign", 35, 35, 35, 35);
            Add("o", 111, 111, 111, 111);
            Add("oacute", -1, 151, 243, 243);
            Add("ocircumflex", -1, 153, 244, 244);
            Add("odieresis", -1, 154, 246, 246);
            Add("oe", 250, 207, 156, 156);
            Add("ogonek", 206, 254, -1, 29);
            Add("ograve", -1, 152, 242, 242);
            Add("one", 49, 49, 49, 49);
            Add("onehalf", -1, -1, 189, 189);
            Add("onequarter", -1, -1, 188, 188);
            Add("onesuperior", -1, -1, 185, 185);
            Add("ordfeminine", 227, 187, 170, 170);
            Add("ordmasculine", 235, 188, 186, 186);
            Add("oslash", 249, 191, 248, 248);
            Add("otilde", -1, 155, 245, 245);
            Add("p", 112, 112, 112, 112);
            Add("paragraph", 182, 166, 182, 182);
            Add("parenleft", 40, 40, 40, 40);
            Add("parenright", 41, 41, 41, 41);
            Add("percent", 37, 37, 37, 37);
            Add("period", 46, 46, 46, 46);
            Add("periodcentered", 180, 225, 183, 183);
            Add("perthousand", 189, 228, 137, 139);
            Add("plus", 43, 43, 43, 43);
            Add("plusminus", -1, 177, 177, 177);
            Add("q", 113, 113, 113, 113);
            Add("question", 63, 63, 63, 63);
            Add("questiondown", 191, 192, 191, 191);
            Add("quotedbl", 34, 34, 34, 34);
            Add("quotedblbase", 185, 227, 132, 140);
            Add("quotedblleft", 170, 210, 147, 141);
            Add("quotedblright", 186, 211, 148, 142);
            Add("quoteleft", 96, 212, 145, 143);
            Add("quoteright", 39, 213, 146, 144);
            Add("quotesinglbase", 184, 226, 130, 145);
            Add("quotesingle", 169, 39, 39, 39);
            Add("r", 114, 114, 114, 114);
            Add("registered", -1, 168, 174, 174);
            Add("ring", 202, 251, -1, 30);
            Add("s", 115, 115, 115, 115);
            Add("scaron", -1, -1, 154, 157);
            Add("section", 167, 164, 167, 167);
            Add("semicolon", 59, 59, 59, 59);
            Add("seven", 55, 55, 55, 55);
            Add("six", 54, 54, 54, 54);
            Add("slash", 47, 47, 47, 47);
            Add("space", 32, 32, 32, 32);
            Add("sterling", 163, 163, 163, 163);
            Add("t", 116, 116, 116, 116);
            Add("thorn", -1, -1, 254, 254);
            Add("three", 51, 51, 51, 51);
            Add("threequarters", -1, -1, 190, 190);
            Add("threesuperior", -1, -1, 179, 179);
            Add("tilde", 196, 247, 152, 31);
            Add("trademark", -1, 170, 153, 146);
            Add("two", 50, 50, 50, 50);
            Add("twosuperior", -1, -1, 178, 178);
            Add("u", 117, 117, 117, 117);
            Add("uacute", -1, 156, 250, 250);
            Add("ucircumflex", -1, 158, 251, 251);
            Add("udieresis", -1, 159, 252, 252);
            Add("ugrave", -1, 157, 249, 249);
            Add("underscore", 95, 95, 95, 95);
            Add("v", 118, 118, 118, 118);
            Add("w", 119, 119, 119, 119);
            Add("x", 120, 120, 120, 120);
            Add("y", 121, 121, 121, 121);
            Add("yacute", -1, -1, 253, 253);
            Add("ydieresis", -1, 216, 255, 255);
            Add("yen", 165, 180, 165, 165);
            Add("z", 122, 122, 122, 122);
            Add("zcaron", -1, -1, 158, 158);
            Add("zero", 48, 48, 48, 48);

            // add 15 additional entries to MacOSEncoding and replace currency with Euro
            MacOSRomanEncoding[173] = "notequal";
            MacOSRomanEncoding[176] = "infinity";
            MacOSRomanEncoding[178] = "lessequal";
            MacOSRomanEncoding[179] = "greaterequal";
            MacOSRomanEncoding[182] = "partialdiff";
            MacOSRomanEncoding[183] = "summation";
            MacOSRomanEncoding[184] = "product";
            MacOSRomanEncoding[185] = "pi";
            MacOSRomanEncoding[186] = "integral";
            MacOSRomanEncoding[189] = "Omega";
            MacOSRomanEncoding[195] = "radical";
            MacOSRomanEncoding[197] = "approxequal";
            MacOSRomanEncoding[198] = "Delta";
            MacOSRomanEncoding[215] = "lozenge";
            MacOSRomanEncoding[219] = "Euro";
            MacOSRomanEncoding[240] = "apple";
        }

        private static void Add(string name, int std, int mac, int win, int pdf)
        {
            if (std != 1) StandardEncoding[std] = name;
            if (mac != -1){
                MacRomanEncoding[mac] = name;
                MacOSRomanEncoding[mac] = name;
            }
            if (win != -1) WinAnsiEncoding[win] = name;
            if (pdf != -1) PdfDocEncoding[pdf] = name;
        }

        /// <summary>
        /// Gets the glyph name for specified character.
        /// </summary>
        /// <value></value>
        public string this[int c]
        {
            get { return this.map[c]; }
            internal set
            {
                this.map[c] = value;
                this.mapBack[value] = c;
            }
        }

        /// <summary>
        /// Gets the glyph name for specified character.
        /// </summary>
        /// <param name="c">The c.</param>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public bool TryGetName(int c, out string name)
        {
            return this.map.TryGetValue(c, out name);
        }

        /// <summary>
        /// Gets the character for specified glyph name.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <param name="c">The character.</param>
        /// <returns></returns>
        public bool TryGetChar(string name, out int c)
        {
            return this.mapBack.TryGetValue(name, out c);
        }

        /// <summary>
        /// Adobe standard Latin-text encoding.
        /// </summary>
        /// <value>The standard encoding.</value>
        public static CharacterToGlyphNameMap StandardEncoding { get; private set; }
        /// <summary>
        /// Mac OS standard encoding for Latin text in Western writing systems.
        /// </summary>
        /// <value>The mac roman encoding.</value>
        public static CharacterToGlyphNameMap MacRomanEncoding { get; private set; }
        /// <summary>
        /// Standard Roman encoding used on Mac OS.
        /// </summary>
        /// <value>The mac roman encoding.</value>
        public static CharacterToGlyphNameMap MacOSRomanEncoding { get; private set; }
        /// <summary>
        /// Windows Code Page 1252, often called the “Windows ANSI” encoding.
        /// </summary>
        /// <value>The win ANSI encoding.</value>
        public static CharacterToGlyphNameMap WinAnsiEncoding { get; private set; }
        /// <summary>
        /// Encoding for text strings in a PDF document outside the document’s content streams.
        /// </summary>
        /// <value>The PDF doc encoding.</value>
        public static CharacterToGlyphNameMap PdfDocEncoding { get; private set; }
    }
}
